package packing.treeSearch;

import java.util.List;
import cloning.Cloner;
import Comparator.EmptySpaceDefComparator;
import geometric3D.Point;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectRBTreeMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectSortedMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.ObjectCollection;
import it.unimi.dsi.fastutil.objects.ObjectSortedSet;
import packing.BoxPack;

import packing.Block;
import packing.BlockRelation;
import packing.EmptySpace;
import packing.EmptySpaceBlockPair;
import packing.EmptySpaceDef;
import packing.SimpleBlock;
import packing.State;

public  class Packer{
	
	 protected State currentState;
	 protected String name;
	 protected Cloner cloner;

	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Packer() {
		super();
		cloner = new Cloner();
	}
	public Packer(State currentState) {
		super();
		this.currentState = currentState;
		cloner = new Cloner();
	}
	public State getCurrentState() {
		return currentState;
	}
	
	public State pack(State state ,EmptySpaceBlockPair pair){
		this.currentState = state;
		currentState.getPackedBlockList().add(pair);
		currentState.getXYRectanglePositiveSet().add(pair.getPairMainBlock().getXYRectangle(true));
		currentState.getXYRectangleNegativeSet().add(pair.getPairMainBlock().getXYRectangle(false));

		currentState.getXZRectanglePositiveSet().add(pair.getPairMainBlock().getXZRectangle(true));
		currentState.getXZRectangleNegativeSet().add(pair.getPairMainBlock().getXZRectangle(false));
		
		currentState.getYZRectanglePositiveSet().add(pair.getPairMainBlock().getYZRectangle(true));
		currentState.getYZRectangleNegativeSet().add(pair.getPairMainBlock().getYZRectangle(false));
		
		
		currentState.getBoxInputList().get(pair.getBlock().getMainBlock().getBox().getId() - 1).minus(pair.getBlock().getMainBlock().getBoxNumber());
		
		if(!pair.getBlock().isSimpleBlock()){
			currentState.getXYRectanglePositiveSet().add(pair.getPairNextBlock().getXYRectangle(true));
			currentState.getXYRectangleNegativeSet().add(pair.getPairNextBlock().getXYRectangle(false));
	
			currentState.getXZRectanglePositiveSet().add(pair.getPairNextBlock().getXZRectangle(true));
			currentState.getXZRectangleNegativeSet().add(pair.getPairNextBlock().getXZRectangle(false));
			
			currentState.getYZRectanglePositiveSet().add(pair.getPairNextBlock().getYZRectangle(true));
			currentState.getYZRectangleNegativeSet().add(pair.getPairNextBlock().getYZRectangle(false));
			currentState.getBoxInputList().get(pair.getBlock().getNextBlock().getBox().getId() - 1).minus(pair.getBlock().getNextBlock().getBoxNumber());
		
		}
		currentState.updateXYZMin();
		Object2ObjectMap<EmptySpaceDef, EmptySpace> childEmptySpace =  getEmptySpace(pair);
		
		ObjectCollection<EmptySpace> childEmptySpaceList = childEmptySpace.values();
		for(EmptySpace emptySpace : childEmptySpaceList){
			currentState.getEmptySpaceStack().push(emptySpace);
			
		}		
		state.evaluate();
		//TODO: still need to complete this pack method
		return currentState;
	}
	
	
	private List<Block> generateBlock(EmptySpace emptySpace, BoxPack boxpack, boolean simpleOnly, double blockPercentage){
		
		List<Block> blockList = new ObjectArrayList<Block>();
		
		List<SimpleBlock> simpleBlockList = generateSimpleBlock(emptySpace, boxpack);
		for(SimpleBlock simpleBlock : simpleBlockList){
			blockList.add(new Block(simpleBlock));
		}
		if(!simpleOnly){
			
			
			for(SimpleBlock mainBlock : simpleBlockList){
				
				blockList.addAll(generatedLeftBlock(emptySpace, mainBlock,blockPercentage ));
				
				blockList.addAll(generatedInfontBlock(emptySpace, mainBlock,blockPercentage ));
				blockList.addAll(generatedBelowBlock(emptySpace, mainBlock,blockPercentage ));
				
			}
		
			
			
		}
		
		return blockList;
		
		
	}
	
	
	private List<SimpleBlock> generateSimpleBlock(EmptySpace emptyspace, BoxPack boxpack){
		
		int xMax = (int)(emptyspace.getWidth()/boxpack.getWidth());
		int yMax = (int)(emptyspace.getLength()/boxpack.getLength());
		int zMax = (int)(emptyspace.getHeight()/boxpack.getHeight());
		int quantity = currentState.getQuantity(boxpack);
		if(xMax > quantity)xMax = quantity;
		if(yMax > quantity)yMax = quantity;
		
		List<SimpleBlock> combination = new ObjectArrayList<SimpleBlock>();
		if(boxpack.getPressure() != 0){
			int zPressure = (int)(boxpack.getPressure()/boxpack.getWeight());
			if(zMax > zPressure){				
				for (int i = 1; i <= xMax; i++) {
					for (int j = 1; j <= yMax; j++) {
						for (int z = 1; z <= zMax - zPressure; z++) {
							if(i*j*z<= quantity){
								combination.add(new SimpleBlock(boxpack, i, j, z));
							}
						}
					}
				}
				zMax = zPressure;
			}
			if(zMax > quantity)zMax = quantity;
		}else{
			if(zMax > quantity)zMax = quantity;
		}
//		if(zMax > quantity)zMax = quantity;
		
		
		for (int i = 1; i <= xMax; i++) {
			for (int j = 1; j <= yMax; j++) {
				for (int z = 1; z <= zMax; z++) {
					if(i*j*z<= quantity){
						combination.add(new SimpleBlock(boxpack, i, j, z));
					}
					
					
				}
			}
		}		
		
		return combination;
	}
	
	private List<Block> generatedLeftBlock(EmptySpace emptySpace, SimpleBlock mainBlock, double blockPercentage){
		List<Block> combination = new ObjectArrayList<Block>();
		for(BoxPack  currentBoxPack : currentState.getBoxPackSet()){

			combination.addAll(generatedLeftBlock(emptySpace, mainBlock, currentBoxPack,blockPercentage));

		}
		
		
		return combination;
		
	}

	private List<Block> generatedLeftBlock(EmptySpace emptySpace, SimpleBlock mainBlock, BoxPack currentBoxPack, double blockPercentage){
		List<Block> combination = new ObjectArrayList<Block>();

		if(currentState.getQuantity(currentBoxPack)!=0
				&& mainBlock.getYZArea() >= currentBoxPack.getYZArea()
				&& emptySpace.getWidth()-mainBlock.getWidth()>= currentBoxPack.getWidth()
				&& emptySpace.getLength()>= currentBoxPack.getLength()
				&& emptySpace.getHeight()>= currentBoxPack.getHeight()
				&& currentBoxPack.getId()!= mainBlock.getBox().getId() ){
			int xMax = (int)((emptySpace.getWidth() - mainBlock.getWidth())/currentBoxPack.getWidth());
			int yMax = (int)(emptySpace.getLength()/currentBoxPack.getLength());
			int zMax = (int)(emptySpace.getHeight()/currentBoxPack.getHeight());
			int quantity = currentState.getQuantity(currentBoxPack);
			if(xMax > quantity)xMax = quantity;
			if(yMax > quantity)yMax = quantity;
//			if(zMax > quantity)zMax = quantity;
			if(currentBoxPack.getPressure() != 0){
				int zPressure = (int)(currentBoxPack.getPressure()/currentBoxPack.getWeight());
				if(zMax > zPressure){
					for (int i = 1; i <= xMax; i++) {
						for (int j = 1; j <= yMax; j++) {
							for (int z = 1; z <= zMax - zPressure ; z++) {
								if(i*j*z<= quantity){
									
									SimpleBlock nextBlock = new SimpleBlock(currentBoxPack, i, j, z);
									if(mainBlock.getYZArea()>=nextBlock.getYZArea()){
										int xEnvelope = nextBlock.getWidth() + mainBlock.getWidth();
										int yEnvelope = Math.max(mainBlock.getLength(), nextBlock.getLength());
										int zEnvelope = Math.max(mainBlock.getHeight(), nextBlock.getHeight());
										int envelopeVolume = xEnvelope*yEnvelope*zEnvelope;
										double evelopeVolumePercentage = (double)(mainBlock.getVolume()+nextBlock.getVolume())/(double)envelopeVolume;
										if(evelopeVolumePercentage>= blockPercentage){
											Block block2add = new Block(mainBlock, nextBlock, BlockRelation.LEFT);
											
											combination.add(block2add);
										}
									}


								}


							}
						}
					}	
					zMax = zPressure;
				}
				if(zMax > quantity)zMax = quantity;
			}else{
				if(zMax > quantity)zMax = quantity;
			}

			for (int i = 1; i <= xMax; i++) {
				for (int j = 1; j <= yMax; j++) {
					for (int z = 1; z <= zMax; z++) {
						if(i*j*z<= quantity){
							
							SimpleBlock nextBlock = new SimpleBlock(currentBoxPack, i, j, z);
							if(mainBlock.getYZArea()>=nextBlock.getYZArea()){
								int xEnvelope = nextBlock.getWidth() + mainBlock.getWidth();
								int yEnvelope = Math.max(mainBlock.getLength(), nextBlock.getLength());
								int zEnvelope = Math.max(mainBlock.getHeight(), nextBlock.getHeight());
								int envelopeVolume = xEnvelope*yEnvelope*zEnvelope;
								double evelopeVolumePercentage = (double)(mainBlock.getVolume()+nextBlock.getVolume())/(double)envelopeVolume;
								if(evelopeVolumePercentage>= blockPercentage){
									Block block2add = new Block(mainBlock, nextBlock, BlockRelation.LEFT);
									
									combination.add(block2add);
								}
							}


						}


					}
				}
			}		

		}




		return combination;
	}
	
	
	
	
	private List<Block> generatedInfontBlock(EmptySpace emptySpace, SimpleBlock mainBlock, double blockPercentage){
		List<Block> combination = new ObjectArrayList<Block>();
		for(BoxPack  currentBoxPack : currentState.getBoxPackSet()){
			combination.addAll(generatedInfontBlock(emptySpace, mainBlock, currentBoxPack, blockPercentage));

		}
		
		
		return combination;
		
	}
	
	private List<Block> generatedInfontBlock(EmptySpace emptySpace, SimpleBlock mainBlock, BoxPack currentBoxPack, double blockPercentage){
		List<Block> combination = new ObjectArrayList<Block>();

		if(currentState.getQuantity(currentBoxPack)!=0
				&& mainBlock.getXZArea() >= currentBoxPack.getXZArea()
				&& emptySpace.getWidth() >= currentBoxPack.getWidth()
				&& emptySpace.getLength() - mainBlock.getLength()>= currentBoxPack.getLength()
				&& emptySpace.getHeight()>= currentBoxPack.getHeight()
				&& currentBoxPack.getId()!= mainBlock.getBox().getId() ){
			int xMax = (int)(emptySpace.getWidth()/currentBoxPack.getWidth());
			int yMax = (int)((emptySpace.getLength()-mainBlock.getLength())/currentBoxPack.getLength());
			int zMax = (int)(emptySpace.getHeight()/currentBoxPack.getHeight());
			int quantity = currentState.getQuantity(currentBoxPack);
			if(xMax > quantity)xMax = quantity;
			if(yMax > quantity)yMax = quantity;
//			if(zMax > quantity)zMax = quantity;
			if(currentBoxPack.getPressure() != 0){
				int zPressure = (int)(currentBoxPack.getPressure()/currentBoxPack.getWeight());
				if(zMax > zPressure){
					for (int i = 1; i <= xMax; i++) {
						for (int j = 1; j <= yMax; j++) {
							for (int z = 1; z <= zMax - zPressure ; z++) {
								if(i*j*z<= quantity){

									SimpleBlock nextBlock = new SimpleBlock(currentBoxPack, i, j, z);
									if(mainBlock.getXZArea()>=nextBlock.getXZArea()){
										int xEnvelope = Math.max(nextBlock.getWidth(),mainBlock.getWidth());
										int yEnvelope = mainBlock.getLength()+nextBlock.getLength();
										int zEnvelope = Math.max(mainBlock.getHeight(), nextBlock.getHeight());
										double evelopeVolumePercentage = (double)(mainBlock.getVolume()+nextBlock.getVolume())/(double)(xEnvelope*yEnvelope*zEnvelope);
										if(evelopeVolumePercentage>= blockPercentage)combination.add(new Block(mainBlock, nextBlock, BlockRelation.INFRONT));
									}


								}


							}
						}
					}	
					zMax = zPressure;
				}
				if(zMax > quantity)zMax = quantity;
			}else{
				if(zMax > quantity)zMax = quantity;
			}

			for (int i = 1; i <= xMax; i++) {
				for (int j = 1; j <= yMax; j++) {
					for (int z = 1; z <= zMax; z++) {
						if(i*j*z<= quantity){

							SimpleBlock nextBlock = new SimpleBlock(currentBoxPack, i, j, z);
							if(mainBlock.getXZArea()>=nextBlock.getXZArea()){
								int xEnvelope = Math.max(nextBlock.getWidth(),mainBlock.getWidth());
								int yEnvelope = mainBlock.getLength()+nextBlock.getLength();
								int zEnvelope = Math.max(mainBlock.getHeight(), nextBlock.getHeight());
								double evelopeVolumePercentage = (double)(mainBlock.getVolume()+nextBlock.getVolume())/(double)(xEnvelope*yEnvelope*zEnvelope);
								if(evelopeVolumePercentage>= blockPercentage)combination.add(new Block(mainBlock, nextBlock, BlockRelation.INFRONT));
							}


						}


					}
				}
			}		



		}


		return combination;
	}

	
	private List<Block> generatedBelowBlock(EmptySpace emptySpace, SimpleBlock mainBlock, double blockPercentage){
		List<Block> combination = new ObjectArrayList<Block>();
		for(BoxPack  currentBoxPack : currentState.getBoxPackSet()){
			combination.addAll(generatedBelowBlock(emptySpace, mainBlock, currentBoxPack, blockPercentage));

		}
		
		return combination;
		
	}
	private List<Block> generatedBelowBlock(EmptySpace emptySpace, SimpleBlock mainBlock, BoxPack currentBoxPack, double blockPercentage){
		List<Block> combination = new ObjectArrayList<Block>();
	
			if(currentState.getQuantity(currentBoxPack)!=0
				&& mainBlock.getXYArea() >= currentBoxPack.getXYArea()
				&& emptySpace.getWidth() >= currentBoxPack.getWidth()
				&& emptySpace.getLength()>= currentBoxPack.getLength()
				&& emptySpace.getHeight()-mainBlock.getHeight()>= currentBoxPack.getHeight()
				&& currentBoxPack.getId()!= mainBlock.getBox().getId() ){
				int xMax = (int)(emptySpace.getWidth()/currentBoxPack.getWidth());
				int yMax = (int)(emptySpace.getLength()/currentBoxPack.getLength());
				int zMax = (int)((emptySpace.getHeight()-mainBlock.getHeight())/currentBoxPack.getHeight());
				int quantity = currentState.getQuantity(currentBoxPack);
				if(xMax > quantity)xMax = quantity;
				if(yMax > quantity)yMax = quantity;
//				if(zMax > quantity)zMax = quantity;
				if(currentBoxPack.getPressure() != 0){
					int zPressure = (int)(currentBoxPack.getPressure()/currentBoxPack.getWeight());
					if(zMax > zPressure){
						for (int i = 1; i <= xMax; i++) {
							for (int j = 1; j <= yMax; j++) {
								for (int z = 1; z <= zMax - zPressure ; z++) {
									if(i*j*z<= quantity){
										
									
										SimpleBlock nextBlock = new SimpleBlock(currentBoxPack, i, j, z);
										if(mainBlock.getXYArea()>= nextBlock.getXYArea()){
											if(mainBlock.getBox().getXYArea() >= nextBlock.getBox().getXYArea()){
												if(mainBlock.getBox().getPressure() >= nextBlock.getSumAllWeight()){
													int xEnvelope = Math.max(nextBlock.getWidth(),mainBlock.getWidth());
													int yEnvelope = Math.max(mainBlock.getLength(),nextBlock.getLength());
													int zEnvelope = mainBlock.getHeight()+nextBlock.getHeight();
													double evelopeVolumePercentage = (double)(mainBlock.getVolume()+nextBlock.getVolume())/(double)(xEnvelope*yEnvelope*zEnvelope);
													if(evelopeVolumePercentage>= blockPercentage)combination.add(new Block(mainBlock, nextBlock, BlockRelation.BELOW));
												}
											}
										}
									}
								}
							}
						}
						zMax = zPressure;
					}
					if(zMax > quantity)zMax = quantity;
				}else{
					if(zMax > quantity)zMax = quantity;
				}
			
				for (int i = 1; i <= xMax; i++) {
					for (int j = 1; j <= yMax; j++) {
						for (int z = 1; z <= zMax; z++) {
							if(i*j*z<= quantity){
								
							
								SimpleBlock nextBlock = new SimpleBlock(currentBoxPack, i, j, z);
								if(mainBlock.getXYArea()>= nextBlock.getXYArea()){
									if(mainBlock.getBox().getXYArea() >= nextBlock.getBox().getXYArea()){
										if(mainBlock.getBox().getPressure() >= nextBlock.getSumAllWeight()){
											int xEnvelope = Math.max(nextBlock.getWidth(),mainBlock.getWidth());
											int yEnvelope = Math.max(mainBlock.getLength(),nextBlock.getLength());
											int zEnvelope = mainBlock.getHeight()+nextBlock.getHeight();
											double evelopeVolumePercentage = (double)(mainBlock.getVolume()+nextBlock.getVolume())/(double)(xEnvelope*yEnvelope*zEnvelope);
											if(evelopeVolumePercentage>= blockPercentage)combination.add(new Block(mainBlock, nextBlock, BlockRelation.BELOW));
										}
									}
								}
							}
						}
					}
				}
			}

		return combination;
	}
	private Object2ObjectMap<EmptySpaceDef,EmptySpace> getEmptySpace(EmptySpaceBlockPair emptySpaceBlockPair){
		
		EmptySpace emptySpace = emptySpaceBlockPair.getEmptySpace();
		Block block = emptySpaceBlockPair.getBlock();
		int rmx= emptySpace.getWidth() - block.getWidth();
		int rmy = emptySpace.getLength() - block.getLength();
		int rmz = emptySpace.getHeight() - block.getHeight();
		int minX = currentState.getxMinSize();
		int minY = currentState.getyMinSize();
		int minZ = currentState.getzMinSize();
		EmptySpaceDef defX;
		EmptySpaceDef defY;
		EmptySpaceDef defZ;
		if(compare(rmx, rmy, minY)){
			//rmx > rmy
			if(compare(rmz, rmx, minX)){
				//rmz>rmx>rmy
				defX = EmptySpaceDef.MED;
				defY = EmptySpaceDef.MIN;
				defZ = EmptySpaceDef.MAX;
				
				
				
			}else if(compare(rmy, rmz, minZ)){
				//rmx >rmy > rmz
				defX = EmptySpaceDef.MAX;
				defY = EmptySpaceDef.MED;
				defZ = EmptySpaceDef.MIN;
			}else {
				//rmx > rmz >rmy
				defX = EmptySpaceDef.MAX;
				defY = EmptySpaceDef.MIN;
				defZ = EmptySpaceDef.MED;
			}
		}else{
			// rmy> rmx
			if(compare(rmz, rmy, minY)){
				//rmz > rmy > rmx
				defX = EmptySpaceDef.MIN;
				defY = EmptySpaceDef.MED;
				defZ = EmptySpaceDef.MAX;
				
			
			}else if(compare(rmx, rmz, minZ)){
				//rmy >rmx > rmz 
				defX = EmptySpaceDef.MED;
				defY = EmptySpaceDef.MAX;
				defZ = EmptySpaceDef.MIN;
				
			}else{
				//rmy>rmz>rmx
				defX = EmptySpaceDef.MIN;
				defY = EmptySpaceDef.MAX;
				defZ = EmptySpaceDef.MED;
				
			}
			
		}
		
		
		 Object2ObjectMap<EmptySpaceDef,EmptySpace>  childEmptySpaces = getEmptySpace(emptySpaceBlockPair, defX, defY, defZ);
		
		
		
		
		
		
		return childEmptySpaces;
		
	}
	
	private boolean compare(int rm1, int rm2, int min){
		
		if(rm1>=rm2 ||rm2<min) return true;
		return false;
		
	}
	
	
	/**
	 * generated the children block from the emptyspace block pair. Return an array of EmptySpace in x y z order
	 * @param emptySpaceBlockPair
	 * @param defX
	 * @param defY
	 * @param defZ
	 * @return
	 */
	private  Object2ObjectMap<EmptySpaceDef,EmptySpace> getEmptySpace(EmptySpaceBlockPair emptySpaceBlockPair, EmptySpaceDef defX, EmptySpaceDef defY, EmptySpaceDef defZ){
		
		EmptySpace emptySpace = emptySpaceBlockPair.getEmptySpace();
		Block block = emptySpaceBlockPair.getBlock();
		int rmx= emptySpace.getWidth() - block.getWidth();
		int rmy = emptySpace.getLength() - block.getLength();
		int rmz = emptySpace.getHeight() - block.getHeight();
		
		Point xPosition = new Point(emptySpace.getPosition().getX()+block.getWidth(), 
				emptySpace.getPosition().getY(), 
				emptySpace.getPosition().getZ());
		
		Point yPosition = new Point(emptySpace.getPosition().getX(), 
									emptySpace.getPosition().getY()+block.getLength(), 
									emptySpace.getPosition().getZ());
		
		Point zPosition = new Point(emptySpace.getPosition().getX(), 
				emptySpace.getPosition().getY(), 
				emptySpace.getPosition().getZ()+block.getHeight());
		EmptySpace xEmptySpace = null;
		EmptySpace yEmptySpace = null;
		EmptySpace zEmptySpace = null;
		if(defX == EmptySpaceDef.MAX 
				&& defY == EmptySpaceDef.MED
				&& defZ == EmptySpaceDef.MIN){
			xEmptySpace = new EmptySpace(xPosition, rmx,emptySpace.getLength(), emptySpace.getHeight());
			yEmptySpace = new EmptySpace(yPosition, block.getWidth(),rmy, emptySpace.getHeight());
			zEmptySpace = new EmptySpace(zPosition, block.getWidth(), block.getLength(), rmz);
		}else if(defX == EmptySpaceDef.MED 
					&& defY == EmptySpaceDef.MAX
					&& defZ == EmptySpaceDef.MIN){
			xEmptySpace = new EmptySpace(xPosition, rmx, block.getLength(),emptySpace.getHeight());
			yEmptySpace = new EmptySpace(yPosition, emptySpace.getWidth(),rmy, emptySpace.getHeight());
			zEmptySpace = new EmptySpace(zPosition, block.getWidth(), block.getLength(), rmz);
			
		}else if(defX == EmptySpaceDef.MAX 
				&& defY == EmptySpaceDef.MIN
				&& defZ == EmptySpaceDef.MED){
			xEmptySpace = new EmptySpace(xPosition, rmx, emptySpace.getLength(),emptySpace.getHeight());
			yEmptySpace = new EmptySpace(yPosition, block.getWidth(),rmy, block.getHeight());
			zEmptySpace = new EmptySpace(zPosition, block.getWidth(), emptySpace.getLength(), rmz);
		
		}else if(defX == EmptySpaceDef.MED 
				&& defY == EmptySpaceDef.MIN
				&& defZ == EmptySpaceDef.MAX){
			xEmptySpace = new EmptySpace(xPosition, rmx, emptySpace.getLength(),block.getHeight());
			yEmptySpace = new EmptySpace(yPosition, block.getWidth(),rmy, block.getHeight());
			zEmptySpace = new EmptySpace(zPosition, emptySpace.getWidth(), emptySpace.getLength(), rmz);
		}else if(defX == EmptySpaceDef.MIN 
				&& defY == EmptySpaceDef.MAX
				&& defZ == EmptySpaceDef.MED){
			xEmptySpace = new EmptySpace(xPosition, rmx, block.getLength(),block.getHeight());
			yEmptySpace = new EmptySpace(yPosition, emptySpace.getWidth(),rmy, emptySpace.getHeight());
			zEmptySpace = new EmptySpace(zPosition, emptySpace.getWidth(), block.getLength(), rmz);
		}else if(defX == EmptySpaceDef.MIN 
				&& defY == EmptySpaceDef.MED
				&& defZ == EmptySpaceDef.MAX){
			xEmptySpace = new EmptySpace(xPosition, rmx, block.getLength(),block.getHeight());
			yEmptySpace = new EmptySpace(yPosition, emptySpace.getWidth(),rmy, block.getHeight());
			zEmptySpace = new EmptySpace(zPosition, emptySpace.getWidth(), emptySpace.getLength(), rmz);
		}
		
		return transferVolume(emptySpaceBlockPair,xEmptySpace,yEmptySpace,zEmptySpace,defX,defY,defZ);
		
	}
	
	private boolean isFillable(EmptySpace emptySpace){
		for(BoxPack boxpack : currentState.getBoxPackSet()){
			if(boxpack.getWidth()<= emptySpace.getWidth()
					&& boxpack.getLength()<= emptySpace.getLength()
					&& boxpack.getHeight()<= emptySpace.getHeight())return true;
			
		}
		return false;
		
	}
//	public void addEmptySpace (List<EmptySpace> emptySpaces,EmptySpace emptySpace, EmptySpaceDef emptySpaceDef){
//
//		if(emptySpaceDef==EmptySpaceDef.MAX){
//			emptySpaces.set(0, emptySpace);
//		}else if(emptySpaceDef==EmptySpaceDef.MED){
//			emptySpaces.set(1, emptySpace);
//		}else{
//			emptySpaces.set(2, emptySpace);
//		}
//		
//	}
	
	private  Object2ObjectSortedMap<EmptySpaceDef,EmptySpace> transferVolume(EmptySpaceBlockPair emptySpaceBlockPair,EmptySpace xEmptySpace, EmptySpace yEmptySpace, EmptySpace zEmptySpace, EmptySpaceDef defX, EmptySpaceDef defY, EmptySpaceDef defZ){
		EmptySpace emptySpace = emptySpaceBlockPair.getEmptySpace();
		EmptySpace [] emptySpaces = {xEmptySpace, yEmptySpace, zEmptySpace};
		EmptySpaceDef [] emptySpaceDefs = {defX, defY, defZ};
		Object2ObjectSortedMap<EmptySpaceDef, EmptySpace> emptySpaceMap = 
			new Object2ObjectRBTreeMap<EmptySpaceDef, EmptySpace>(emptySpaceDefs, emptySpaces,new EmptySpaceDefComparator());
		
	
		
		
		if(defZ == EmptySpaceDef.MIN){
			
			int minWidth =  zEmptySpace.getWidth();
			int minLength = zEmptySpace.getLength(); 
			if(!isFillable(xEmptySpace)){
				minWidth = emptySpace.getWidth();
				emptySpaceMap.remove(defX);
				
			}
			if(!isFillable(yEmptySpace)){
				minLength = emptySpace.getLength();
				emptySpaceMap.remove(defY);
			}
			emptySpaceMap.remove(defZ);
			zEmptySpace= new EmptySpace(zEmptySpace.getPosition(),minWidth, minLength, zEmptySpace.getHeight());
			emptySpaceMap.put(defZ, zEmptySpace);
			
		}else if(defY == EmptySpaceDef.MIN){
			int minWidth =  yEmptySpace.getWidth();
			int minHeight = yEmptySpace.getHeight(); 
			if(!isFillable(xEmptySpace)){
				minWidth = emptySpace.getWidth();
				emptySpaceMap.remove(defX);
			}
			if(!isFillable(zEmptySpace)){
				minHeight = emptySpace.getHeight();
				emptySpaceMap.remove(defZ);
			}
			emptySpaceMap.remove(defY);
			yEmptySpace= new EmptySpace(yEmptySpace.getPosition(),minWidth, yEmptySpace.getLength(), minHeight);
			emptySpaceMap.put(defY, yEmptySpace);
		
		}else if(defX == EmptySpaceDef.MIN){
			int minLength = xEmptySpace.getLength();
			int minHeight = xEmptySpace.getHeight(); 
			if(!isFillable(yEmptySpace)){
				minLength = emptySpace.getLength();
				emptySpaceMap.remove(defY);
			}
			if(!isFillable(zEmptySpace)){
				minHeight = emptySpace.getHeight();
				emptySpaceMap.remove(defZ);
			}
			emptySpaceMap.remove(defX);
			xEmptySpace= new EmptySpace(xEmptySpace.getPosition(),xEmptySpace.getWidth(), minLength, minHeight);
			emptySpaceMap.put(defX, xEmptySpace);
		}
			
		return emptySpaceMap;
		
		
	}
	
//	private List<Block> getValidBlock(EmptySpace emptySpace, List<Block> blockList){
//		List<Block> fitBlockList = new ObjectArrayList<Block>();
//		for(Block currentblock : blockList){
//			if(isValid(emptySpace, currentblock))fitBlockList.add(currentblock);
//			
//		}
//		return fitBlockList;
//		
//	}
	
//	private boolean isValid(EmptySpace emptySpace, Block block){
//		if(block.getWidth()>emptySpace.getWidth()
//				||block.getLength() > emptySpace.getLength()
//				||block.getHeight() > emptySpace.getHeight()) return false;
//		return true;
//	}
	
	private List<BoxPack> getValidBoxPack(EmptySpace emptySpace, ObjectSortedSet<BoxPack> boxPackList){
		List<BoxPack> fitBoxPack = new ObjectArrayList<BoxPack>();
		
		System.out.println("\nเกิดรูปแบบการวางทั้งหมด ( Box Pack List ) จำนวน  "+boxPackList.size()+" รายการ ดังนี้  :");
		System.out.println("======================================================================");
		
		int count = 1;
		for(BoxPack currentBoxPack : boxPackList){
			System.out.print("ลำดับที่ "+count+" : "+currentBoxPack.toString());
			if(isValid(emptySpace, currentBoxPack))fitBoxPack.add(currentBoxPack);
			count++;
		}
		return fitBoxPack;
		
	}
	
	private boolean isValid(EmptySpace emptySpace, BoxPack boxpack){
		if(boxpack.getWidth()> emptySpace.getWidth()
				|| boxpack.getLength() >emptySpace.getLength()
				|| boxpack.getHeight() > emptySpace.getHeight()
				|| currentState.getQuantity(boxpack)<=0){
			System.out.println("สถานะ -- ไม่ผ่าน --\n");
			return false;
		}
		System.out.println("สถานะ -- ผ่าน --\n");
		return true;		
	}	

	public List<Block> getFitableBlock(State state, EmptySpace emptySpace, boolean simpleOnly, double blockPercentage){
		this.currentState = state;
		List<BoxPack> fitBoxPack = getValidBoxPack(emptySpace, currentState.getBoxPackSet());
		List<Block> blockList = new ObjectArrayList<Block>();
		for(BoxPack boxpack : fitBoxPack){
			blockList.addAll(generateBlock(emptySpace, boxpack, simpleOnly, blockPercentage));
			
		}		
		return blockList;
		
		
		
	}

	
	
	
	public State copy(State state){
		State newState = cloner.deepClone(state);
		return newState;
		
		
	}
	
	


	
	


}
